package com.gitee.wsl.collections.map.multi

import com.gitee.wsl.base.Objects.equals
import com.gitee.wsl.collections.bean.HashEntry
import com.gitee.wsl.collections.bean.MultiKey
import com.gitee.wsl.collections.delegate.DelegatingMutableMap
import com.gitee.wsl.collections.iterator.api.MutableMapIterator
import com.gitee.wsl.collections.map.AbstractHashedMap
import com.gitee.wsl.collections.map.HashedMap


/**
 * A `Map` implementation that uses multiple keys to map the value.
 *
 *
 * This class is the most efficient way to uses multiple keys to map to a value.
 * The best way to use this class is via the additional map-style methods.
 * These provide `get`, `containsKey`, `put` and
 * `remove` for individual keys which operate without extra object creation.
 *
 *
 *
 * The additional methods are the main interface of this map.
 * As such, you will not normally hold this map in a variable of type `Map`.
 *
 *
 *
 * The normal map methods take in and return a [MultiKey].
 * If you try to use `put()` with any other object type a
 * `ClassCastException` is thrown. If you try to use `null` as
 * the key in `put()` a `NullPointerException` is thrown.
 *
 *
 *
 * This map is implemented as a decorator of a `AbstractHashedMap` which
 * enables extra behavior to be added easily.
 *
 *
 *  * `MultiKeyMap.decorate(new LinkedMap())` creates an ordered map.
 *  * `MultiKeyMap.decorate(new LRUMap())` creates an least recently used map.
 *  * `MultiKeyMap.decorate(new ReferenceMap())` creates a garbage collector sensitive map.
 *
 *
 *
 * Note that `IdentityMap` and `ReferenceIdentityMap` are unsuitable
 * for use as the key comparison would work on the whole MultiKey, not the elements within.
 *
 *
 *
 * As an example, consider a least recently used cache that uses a String airline code
 * and a Locale to lookup the airline's name:
 *
 * <pre>
 * private MultiKeyMap cache = MultiKeyMap.multiKeyMap(new LRUMap(50));
 *
 * public String getAirlineName(String code, String locale) {
 * String name = (String) cache.get(code, locale);
 * if (name == null) {
 * name = getAirlineNameFromDB(code, locale);
 * cache.put(code, locale, name);
 * }
 * return name;
 * }
</pre> *
 *
 *
 * **Note that MultiKeyMap is not synchronized and is not thread-safe.**
 * If you wish to use this map from multiple threads concurrently, you must use
 * appropriate synchronization. This class may throw exceptions when accessed
 * by concurrent threads without synchronization.
 *
 *
 * @param <K> the type of the keys in this map
 * @param <V> the type of the values in this map
 * @since 3.1
</V></K> */
class MultiKeyMap<K, V> (
    map: AbstractHashedMap<MultiKey<K>, V> = HashedMap()
) : DelegatingMutableMap<MultiKey<K>, V>(map) {

    /**
     * Check to ensure that input keys are valid MultiKey objects.
     *
     * @param key  the key to check
     */
    protected fun checkKey(key: MultiKey<K>) {
        //Objects.requireNonNull(key, "key")
    }

    /**
     * Clones the map without cloning the keys or values.
     *
     * @return a shallow clone
     */
//    public override fun clone(): MultiKeyMap<K, V> {
//        try {
//            return super.clone() as MultiKeyMap<K?, V?>?
//        } catch (e: java.lang.CloneNotSupportedException) {
//            throw java.lang.UnsupportedOperationException(e)
//        }
//    }

    /**
     * Checks whether the map contains the specified multi-key.
     *
     * @param key1  the first key
     * @param key2  the second key
     * @return true if the map contains the key
     */
    fun containsKey(key1: K, key2: K): Boolean {
        val hashCode = hash(key1, key2)
        var entry = decoratedHashEntry(hashCode)
        while (entry != null) {
            if (entry.hashCode == hashCode && isEqualKey(entry, key1, key2)) {
                return true
            }
            entry = entry.next
        }
        return false
    }

    /**
     * Checks whether the map contains the specified multi-key.
     *
     * @param key1  the first key
     * @param key2  the second key
     * @param key3  the third key
     * @return true if the map contains the key
     */
    fun containsKey(key1: K, key2: K, key3: K): Boolean {
        val hashCode = hash(key1, key2, key3)
        var entry = decoratedHashEntry(hashCode)
        while (entry != null) {
            if (entry.hashCode == hashCode && isEqualKey(entry, key1, key2, key3)) {
                return true
            }
            entry = entry.next
        }
        return false
    }

    /**
     * Checks whether the map contains the specified multi-key.
     *
     * @param key1  the first key
     * @param key2  the second key
     * @param key3  the third key
     * @param key4  the fourth key
     * @return true if the map contains the key
     */
    fun containsKey(key1: K, key2: K, key3: K, key4: K): Boolean {
        val hashCode = hash(key1, key2, key3, key4)
        var entry = decoratedHashEntry(hashCode)
        while (entry != null) {
            if (entry.hashCode == hashCode && isEqualKey(entry, key1, key2, key3, key4)) {
                return true
            }
            entry = entry.next
        }
        return false
    }

    /**
     * Checks whether the map contains the specified multi-key.
     *
     * @param key1  the first key
     * @param key2  the second key
     * @param key3  the third key
     * @param key4  the fourth key
     * @param key5  the fifth key
     * @return true if the map contains the key
     */
    fun containsKey(key1: K, key2: K, key3: K, key4: K, key5: K): Boolean {
        val hashCode = hash(key1, key2, key3, key4, key5)
        var entry: HashEntry<MultiKey<K>, V>? = decoratedHashEntry(hashCode)
        while (entry != null) {
            if (entry.hashCode == hashCode && isEqualKey(entry, key1, key2, key3, key4, key5)) {
                return true
            }
            entry = entry.next
        }
        return false
    }

    /**
     * {@inheritDoc}
     */
    protected  fun decorated() = delegate as AbstractHashedMap<MultiKey<K>, V>

    fun decoratedHashEntry(hashCode: Int): HashEntry<MultiKey<K>, V>? {
        return decorated().data[decoratedHashIndex(hashCode)]
    }

    fun decoratedHashIndex(hashCode: Int): Int {
        return decorated().hashIndex(hashCode, decorated().data.size)
    }

    /**
     * Gets the value mapped to the specified multi-key.
     *
     * @param key1  the first key
     * @param key2  the second key
     * @return the mapped value, null if no match
     */
    fun get(key1: K, key2: K): V? {
        val hashCode = hash(key1, key2)
        var entry = decoratedHashEntry(hashCode)
        while (entry != null) {
            if (entry.hashCode == hashCode && isEqualKey(entry, key1, key2)) {
                return entry.value
            }
            entry = entry.next
        }
        return null
    }

    /**
     * Gets the value mapped to the specified multi-key.
     *
     * @param key1  the first key
     * @param key2  the second key
     * @param key3  the third key
     * @return the mapped value, null if no match
     */
    fun get(key1: K, key2: K, key3: K): V? {
        val hashCode = hash(key1, key2, key3)
        var entry: HashEntry<MultiKey<K>, V>? = decoratedHashEntry(hashCode)
        while (entry != null) {
            if (entry.hashCode == hashCode && isEqualKey(entry, key1, key2, key3)) {
                return entry.value
            }
            entry = entry.next
        }
        return null
    }

    /**
     * Gets the value mapped to the specified multi-key.
     *
     * @param key1  the first key
     * @param key2  the second key
     * @param key3  the third key
     * @param key4  the fourth key
     * @return the mapped value, null if no match
     */
    fun get(key1: K, key2: K, key3: K, key4: K): V? {
        val hashCode = hash(key1, key2, key3, key4)
        var entry = decoratedHashEntry(hashCode)
        while (entry != null) {
            if (entry.hashCode == hashCode && isEqualKey(entry, key1, key2, key3, key4)) {
                return entry.value
            }
            entry = entry.next
        }
        return null
    }

    /**
     * Gets the value mapped to the specified multi-key.
     *
     * @param key1  the first key
     * @param key2  the second key
     * @param key3  the third key
     * @param key4  the fourth key
     * @param key5  the fifth key
     * @return the mapped value, null if no match
     */
    fun get(key1: K, key2: K, key3: K, key4: K, key5: K): V? {
        val hashCode = hash(key1, key2, key3, key4, key5)
        var entry = decoratedHashEntry(hashCode)
        while (entry != null) {
            if (entry.hashCode == hashCode && isEqualKey(entry, key1, key2, key3, key4, key5)) {
                return entry.value
            }
            entry = entry.next
        }
        return null
    }

    /**
     * Gets the hash code for the specified multi-key.
     *
     * @param key1  the first key
     * @param key2  the second key
     * @return the hash code
     */
    protected fun hash(key1: Any?, key2: Any?): Int {
        var h = 0
        if (key1 != null) {
            h = h xor key1.hashCode()
        }
        if (key2 != null) {
            h = h xor key2.hashCode()
        }
        h += (h shl 9).inv()
        h = h xor (h ushr 14)
        h += h shl 4
        h = h xor (h ushr 10)
        return h
    }

    /**
     * Gets the hash code for the specified multi-key.
     *
     * @param key1  the first key
     * @param key2  the second key
     * @param key3  the third key
     * @return the hash code
     */
    protected fun hash(key1: Any?, key2: Any?, key3: Any?): Int {
        var h = 0
        if (key1 != null) {
            h = h xor key1.hashCode()
        }
        if (key2 != null) {
            h = h xor key2.hashCode()
        }
        if (key3 != null) {
            h = h xor key3.hashCode()
        }
        h += (h shl 9).inv()
        h = h xor (h ushr 14)
        h += h shl 4
        h = h xor (h ushr 10)
        return h
    }

    /**
     * Gets the hash code for the specified multi-key.
     *
     * @param key1  the first key
     * @param key2  the second key
     * @param key3  the third key
     * @param key4  the fourth key
     * @return the hash code
     */
    protected fun hash(key1: Any?, key2: Any?, key3: Any?, key4: Any?): Int {
        var h = 0
        if (key1 != null) {
            h = h xor key1.hashCode()
        }
        if (key2 != null) {
            h = h xor key2.hashCode()
        }
        if (key3 != null) {
            h = h xor key3.hashCode()
        }
        if (key4 != null) {
            h = h xor key4.hashCode()
        }
        h += (h shl 9).inv()
        h = h xor (h ushr 14)
        h += h shl 4
        h = h xor (h ushr 10)
        return h
    }

    /**
     * Gets the hash code for the specified multi-key.
     *
     * @param key1  the first key
     * @param key2  the second key
     * @param key3  the third key
     * @param key4  the fourth key
     * @param key5  the fifth key
     * @return the hash code
     */
    protected fun hash(key1: Any?, key2: Any?, key3: Any?, key4: Any?, key5: Any?): Int {
        var h = 0
        if (key1 != null) {
            h = h xor key1.hashCode()
        }
        if (key2 != null) {
            h = h xor key2.hashCode()
        }
        if (key3 != null) {
            h = h xor key3.hashCode()
        }
        if (key4 != null) {
            h = h xor key4.hashCode()
        }
        if (key5 != null) {
            h = h xor key5.hashCode()
        }
        h += (h shl 9).inv()
        h = h xor (h ushr 14)
        h += h shl 4
        h = h xor (h ushr 10)
        return h
    }

    /**
     * Is the key equal to the combined key.
     *
     * @param entry  the entry to compare to
     * @param key1  the first key
     * @param key2  the second key
     * @return true if the key matches
     */
    protected fun isEqualKey(
        entry: HashEntry<MultiKey<K>, V>,
        key1: K, key2: K
    ): Boolean {
        val multi = entry.key
        return multi.size() == 2 &&
                equals(key1, multi.getKey(0)) &&
                equals(key2, multi.getKey(1))
    }

    /**
     * Is the key equal to the combined key.
     *
     * @param entry  the entry to compare to
     * @param key1  the first key
     * @param key2  the second key
     * @param key3  the third key
     * @return true if the key matches
     */
    protected fun isEqualKey(
        entry: HashEntry<MultiKey<K>, V>,
        key1: K, key2: K, key3: K
    ): Boolean {
        val multi = entry.key
        return multi.size() == 3 &&
                equals(key1, multi.getKey(0)) &&
                equals(key2, multi.getKey(1)) &&
                equals(key3, multi.getKey(2))
    }

    /**
     * Is the key equal to the combined key.
     *
     * @param entry  the entry to compare to
     * @param key1  the first key
     * @param key2  the second key
     * @param key3  the third key
     * @param key4  the fourth key
     * @return true if the key matches
     */
    protected fun isEqualKey(
        entry: HashEntry<MultiKey<K>, V>,
        key1: K, key2: K, key3: K, key4: K
    ): Boolean {
        val multi= entry.key
        return multi.size() == 4 &&
                equals(key1, multi.getKey(0)) &&
                equals(key2, multi.getKey(1)) &&
                equals(key3, multi.getKey(2)) &&
                equals(key4, multi.getKey(3))
    }

    /**
     * Is the key equal to the combined key.
     *
     * @param entry  the entry to compare to
     * @param key1  the first key
     * @param key2  the second key
     * @param key3  the third key
     * @param key4  the fourth key
     * @param key5  the fifth key
     * @return true if the key matches
     */
    protected fun isEqualKey(
        entry: HashEntry<MultiKey<K>, V>,
        key1: K, key2: K, key3: K, key4: K, key5: K
    ): Boolean {
        val multi = entry.key
        return multi.size() == 5 &&
                equals(key1, multi.getKey(0)) &&
                equals(key2, multi.getKey(1)) &&
                equals(key3, multi.getKey(2)) &&
                equals(key4, multi.getKey(3)) &&
                equals(key5, multi.getKey(4))
    }

    fun mapIterator(): MutableMapIterator<MultiKey<K>, V> {
        return decorated().mapIterator()
    }

    /**
     * Associates the specified value with the specified keys in this map.
     *
     * @param key1  the first key
     * @param key2  the second key
     * @param key3  the third key
     * @param key4  the fourth key
     * @param key5  the fifth key
     * @param value  the value to store
     * @return the value previously mapped to this combined key, null if none
     */
    fun put(key1: K, key2: K, key3: K, key4: K, key5: K, value: V): V? {
        val hashCode = hash(key1, key2, key3, key4, key5)
        val index = decoratedHashIndex(hashCode)
        var entry = decorated().data[index]
        while (entry != null) {
            if (entry.hashCode == hashCode && isEqualKey(entry, key1, key2, key3, key4, key5)) {
                val oldValue: V? = entry.value
                decorated().updateEntry(entry, value)
                return oldValue
            }
            entry = entry.next
        }
        decorated().addMapping(index, hashCode, MultiKey(key1, key2, key3, key4, key5), value)
        return null
    }

    /**
     * Associates the specified value with the specified keys in this map.
     *
     * @param key1  the first key
     * @param key2  the second key
     * @param key3  the third key
     * @param key4  the fourth key
     * @param value  the value to store
     * @return the value previously mapped to this combined key, null if none
     */
    fun put(key1: K, key2: K, key3: K, key4: K, value: V): V? {
        val hashCode = hash(key1, key2, key3, key4)
        val index = decoratedHashIndex(hashCode)
        var entry: HashEntry<MultiKey<K>, V>? = decorated().data[index]
        while (entry != null) {
            if (entry.hashCode == hashCode && isEqualKey(entry, key1, key2, key3, key4)) {
                val oldValue = entry.value
                decorated().updateEntry(entry, value)
                return oldValue
            }
            entry = entry.next
        }
        decorated().addMapping(index, hashCode, MultiKey(key1, key2, key3, key4), value)
        return null
    }

    /**
     * Associates the specified value with the specified keys in this map.
     *
     * @param key1  the first key
     * @param key2  the second key
     * @param key3  the third key
     * @param value  the value to store
     * @return the value previously mapped to this combined key, null if none
     */
    fun put(key1: K, key2: K, key3: K,  value: V): V? {
        val hashCode = hash(key1, key2, key3)
        val index = decoratedHashIndex(hashCode)
        var entry: HashEntry<MultiKey<K>, V>? = decorated().data[index]
        while (entry != null) {
            if (entry.hashCode == hashCode && isEqualKey(entry, key1, key2, key3)) {
                val oldValue = entry.value
                decorated().updateEntry(entry, value)
                return oldValue
            }
            entry = entry.next
        }
        decorated().addMapping(index, hashCode, MultiKey(key1, key2, key3), value)
        return null
    }

    /**
     * Associates the specified value with the specified keys in this map.
     *
     * @param key1  the first key
     * @param key2  the second key
     * @param value  the value to store
     * @return the value previously mapped to this combined key, null if none
     */
    fun put(key1: K, key2: K, value: V): V? {
        val hashCode = hash(key1, key2)
        val index = decoratedHashIndex(hashCode)
        var entry: HashEntry<MultiKey<K>, V>? = decorated().data[index]
        while (entry != null) {
            if (entry.hashCode == hashCode && isEqualKey(entry, key1, key2)) {
                val oldValue: V? = entry.value
                decorated().updateEntry(entry, value)
                return oldValue
            }
            entry = entry.next
        }
        decorated().addMapping(index, hashCode, MultiKey(key1, key2), value)
        return null
    }

    /**
     * Puts the key and value into the map, where the key must be a non-null
     * MultiKey object.
     *
     * @param key  the non-null MultiKey object
     * @param value  the value to store
     * @return the previous value for the key
     * @throws NullPointerException if the key is null
     * @throws ClassCastException if the key is not a MultiKey
     */
    override fun put(key: MultiKey<K>, value: V): V? {
        checkKey(key)
        return super.put(key, value)
    }

    /**
     * Copies all of the keys and values from the specified map to this map.
     * Each key must be non-null and a MultiKey object.
     *
     * @param from  to this map
     * @throws NullPointerException if the mapToCopy or any key within is null
     * @throws ClassCastException if any key in mapToCopy is not a MultiKey
     */
    public override fun putAll(from: Map<out MultiKey<K>,  V>) {
        for (key in from.keys) {
            checkKey(key)
        }
        super.putAll(from)
    }

    /**
     * Deserializes the map in using a custom routine.
     *
     * @param in  the input stream
     * @throws IOException if an error occurs while reading from the stream
     * @throws ClassNotFoundException if an object read from the stream cannot be loaded
     */
//    @Throws(IOException::class, java.lang.ClassNotFoundException::class)
//    private fun readObject(`in`: ObjectInputStream) {
//        `in`.defaultReadObject()
//        map = `in`.readObject() as Map<MultiKey<K>, V>?
//    }

    /**
     * Removes all mappings where the first key is that specified.
     *
     *
     * This method removes all the mappings where the `MultiKey`
     * has one or more keys, and the first matches that specified.
     *
     *
     * @param key1  the first key
     * @return true if any elements were removed
     */
    fun removeAll(key1: K): Boolean {
        var modified = false
        val it = mapIterator()
        while (it.hasNext()) {
            val multi = it.next()
            if (multi.size() >= 1 &&
                equals<Any?>(key1, multi.getKey(0))
            ) {
                it.remove()
                modified = true
            }
        }
        return modified
    }

    /**
     * Removes all mappings where the first two keys are those specified.
     *
     *
     * This method removes all the mappings where the `MultiKey`
     * has two or more keys, and the first two match those specified.
     *
     *
     * @param key1  the first key
     * @param key2  the second key
     * @return true if any elements were removed
     */
    fun removeAll(key1: K, key2: K): Boolean {
        var modified = false
        val it = mapIterator()
        while (it.hasNext()) {
            val multi = it.next()
            if (multi.size() >= 2 &&
                equals(key1, multi.getKey(0)) &&
                equals(key2, multi.getKey(1))
            ) {
                it.remove()
                modified = true
            }
        }
        return modified
    }

    /**
     * Removes all mappings where the first three keys are those specified.
     *
     *
     * This method removes all the mappings where the `MultiKey`
     * has three or more keys, and the first three match those specified.
     *
     *
     * @param key1  the first key
     * @param key2  the second key
     * @param key3  the third key
     * @return true if any elements were removed
     */
    fun removeAll(key1: K, key2: K, key3: K): Boolean {
        var modified = false
        val it = mapIterator()
        while (it.hasNext()) {
            val multi = it.next()
            if (multi.size() >= 3 &&
                equals(key1, multi.getKey(0)) &&
                equals(key2, multi.getKey(1)) &&
                equals(key3, multi.getKey(2))
            ) {
                it.remove()
                modified = true
            }
        }
        return modified
    }

    /**
     * Removes all mappings where the first four keys are those specified.
     *
     *
     * This method removes all the mappings where the `MultiKey`
     * has four or more keys, and the first four match those specified.
     *
     *
     * @param key1  the first key
     * @param key2  the second key
     * @param key3  the third key
     * @param key4  the fourth key
     * @return true if any elements were removed
     */
    fun removeAll(key1: K, key2: K, key3: K, key4: K): Boolean {
        var modified = false
        val it = mapIterator()
        while (it.hasNext()) {
            val multi = it.next()
            if (multi.size() >= 4 &&
                equals(key1, multi.getKey(0)) &&
                equals(key2, multi.getKey(1)) &&
                equals(key3, multi.getKey(2)) &&
                equals(key4, multi.getKey(3))
            ) {
                it.remove()
                modified = true
            }
        }
        return modified
    }

    /**
     * Removes the specified multi-key from this map.
     *
     * @param key1  the first key
     * @param key2  the second key
     * @return the value mapped to the removed key, null if key not in map
     * @since 4.0 (previous name: remove(Object, Object))
     */
    fun removeMultiKey(key1: K, key2: K): V? {
        val hashCode = hash(key1, key2)
        val index = decoratedHashIndex(hashCode)
        var entry: HashEntry<MultiKey<K>, V>? = decorated().data[index]
        var previous: HashEntry<MultiKey<K>, V>? = null
        while (entry != null) {
            if (entry.hashCode == hashCode && isEqualKey(entry, key1, key2)) {
                val oldValue = entry.value
                decorated().removeMapping(entry, index, previous)
                return oldValue
            }
            previous = entry
            entry = entry.next
        }
        return null
    }

    /**
     * Removes the specified multi-key from this map.
     *
     * @param key1  the first key
     * @param key2  the second key
     * @param key3  the third key
     * @return the value mapped to the removed key, null if key not in map
     * @since 4.0 (previous name: remove(Object, Object, Object))
     */
    fun removeMultiKey(key1: K, key2: K, key3: K): V? {
        val hashCode = hash(key1, key2, key3)
        val index = decoratedHashIndex(hashCode)
        var entry: HashEntry<MultiKey<K>, V>? = decorated().data[index]
        var previous: HashEntry<MultiKey<K>, V>? = null
        while (entry != null) {
            if (entry.hashCode == hashCode && isEqualKey(entry, key1, key2, key3)) {
                val oldValue = entry.value
                decorated().removeMapping(entry, index, previous)
                return oldValue
            }
            previous = entry
            entry = entry.next
        }
        return null
    }

    /**
     * Removes the specified multi-key from this map.
     *
     * @param key1  the first key
     * @param key2  the second key
     * @param key3  the third key
     * @param key4  the fourth key
     * @return the value mapped to the removed key, null if key not in map
     * @since 4.0 (previous name: remove(Object, Object, Object, Object))
     */
    fun removeMultiKey(key1: K, key2: K, key3: K, key4: K): V? {
        val hashCode = hash(key1, key2, key3, key4)
        val index = decoratedHashIndex(hashCode)
        var entry: HashEntry<MultiKey<K>, V>? = decorated().data[index]
        var previous: HashEntry<MultiKey<K>, V>? = null
        while (entry != null) {
            if (entry.hashCode == hashCode && isEqualKey(entry, key1, key2, key3, key4)) {
                val oldValue = entry.value
                decorated().removeMapping(entry, index, previous)
                return oldValue
            }
            previous = entry
            entry = entry.next
        }
        return null
    }

    /**
     * Removes the specified multi-key from this map.
     *
     * @param key1  the first key
     * @param key2  the second key
     * @param key3  the third key
     * @param key4  the fourth key
     * @param key5  the fifth key
     * @return the value mapped to the removed key, null if key not in map
     * @since 4.0 (previous name: remove(Object, Object, Object, Object, Object))
     */
    fun removeMultiKey(
        key1: K, key2: K, key3: K, key4: K, key5: K
    ): V? {
        val hashCode = hash(key1, key2, key3, key4, key5)
        val index = decoratedHashIndex(hashCode)
        var entry: HashEntry<MultiKey<K>, V>? = decorated().data[index]
        var previous: HashEntry<MultiKey<K>, V>? = null
        while (entry != null) {
            if (entry.hashCode == hashCode && isEqualKey(entry, key1, key2, key3, key4, key5)) {
                val oldValue= entry.value
                decorated().removeMapping(entry, index, previous)
                return oldValue
            }
            previous = entry
            entry = entry.next
        }
        return null
    }

    /**
     * Serializes this object to an ObjectOutputStream.
     *
     * @param out the target ObjectOutputStream.
     * @throws IOException thrown when an I/O errors occur writing to the target stream.
     */
//    @Throws(IOException::class)
//    private fun writeObject(out: ObjectOutputStream) {
//        out.defaultWriteObject()
//        out.writeObject(map)
//    }

    companion object {
        
        /**
         * Decorates the specified map to add the MultiKeyMap API and fast query.
         * The map must not be null and must be empty.
         *
         * @param <K>  the key type
         * @param <V>  the value type
         * @param map  the map to decorate, not null
         * @return a new multi key map
         * @throws NullPointerException if map is null
         * @throws IllegalArgumentException if the map is not empty
         * @since 4.0
        </V></K> */
        fun <K, V> multiKeyMap(map: AbstractHashedMap<MultiKey<K>, V>): MultiKeyMap<K, V> {
            if (map.isEmpty()) {
                return MultiKeyMap(map)
            }
            throw IllegalArgumentException("Map must be empty")
        }
    }
}
